#include "rtl.h"

VOID RtlCopyMemory(LPVOID lpvDest, LPCVOID lpvSrc, DWORD dwSize) {
  __asm__("rep movsb" ::"c"(dwSize), "S"(lpvSrc), "D"(lpvDest));
}

VOID RtlFillMemory(LPVOID lpvDest, DWORD dwVal, DWORD dwSize) {
    if (dwVal & 0xFFFF0000) {
        __asm__("rep stosl" ::"a"(dwVal), "c"(dwSize >> 2), "D"(lpvDest));
        return;
    }
    if (dwVal & 0xFF00) {
        __asm__("rep stosw" ::"a"(dwVal), "c"(dwSize >> 1), "D"(lpvDest));
        return;
    }
    __asm__("rep stosb" ::"a"(dwVal), "c"(dwSize), "D"(lpvDest));
}

VOID RtlZeroMemory(LPVOID lpvDest, DWORD dwSize) {
    __asm__("mov $0,%%al\n"
            "rep stosb\n" ::"c"(dwSize),
            "D"(lpvDest));
}

STATIC VOID itoa(DWORD dwVal, LPSTR *ppcBuffer, BYTE bBase) {
    const CHAR cHex[0x10] = {'0', '1', '2', '3', '4', '5', '6', '7',
                            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
    DWORD dwNum = dwVal % bBase;
    DWORD dwIndex = dwVal / bBase;
    if (dwIndex) {
        itoa(dwIndex, ppcBuffer, bBase);
    }
    *((*ppcBuffer)++) = cHex[dwNum];
}

STATIC DWORD atoi(LPSTR *ppcBuffer) {
    BYTE bBase = 10;
    while (**ppcBuffer == ' ')
        ++*ppcBuffer;
    if (**ppcBuffer == '0') {
        if (*(++(*ppcBuffer)) == 'x') {
            bBase = 16;
            ++*ppcBuffer;
        } else
            bBase = 8;
    }
    REG DWORD dwVal = 0;
    while (**ppcBuffer != ' ') {
        CHAR cChar = **ppcBuffer;
        if (!cChar)
            break;
        dwVal *= (DWORD)bBase;
        if (cChar >= '0' && cChar <= '9')
            dwVal += cChar - '0';
        else if (cChar >= 'a' && cChar <= 'f')
            dwVal += cChar - 'a' + 10;
        else if (cChar >= 'A' && cChar <= 'F')
            dwVal += cChar - 'A' + 10;
        ++*ppcBuffer;
    }
    return dwVal;
}

INT RtlVSPrintf(LPSTR lpszBuffer, LPCSTR lpszFormat, va_list lpcList) {
    LPSTR pcBuffer = lpszBuffer;
    LPCSTR pcIndex = lpszFormat;
    CHAR cIndex = *pcIndex;
    INT nArg;
    LPSTR pcArg;
    while (cIndex) {
        if (cIndex != '%') {
            *(pcBuffer++) = cIndex;
            cIndex = *(++pcIndex);
            continue;
        }
        cIndex = *(++pcIndex);
        switch (cIndex) {
        case 's':
            pcArg = va_arg(lpcList, LPSTR );
            RtlStringCopy(pcBuffer, pcArg);
            pcBuffer += RtlStringLength(pcArg);
            cIndex = *(++pcIndex);
            break;
        case 'c':
            *(pcBuffer++) = va_arg(lpcList, CHAR);
            cIndex = *(++pcIndex);
            break;
        case 'd':
            nArg = va_arg(lpcList, INT);
            if (nArg < 0) {
                nArg = 0 - nArg;
                *pcBuffer++ = '-';
            }
            itoa(nArg, &pcBuffer, 10);
            cIndex = *(++pcIndex);
            break;
        case 'x':
            nArg = va_arg(lpcList, INT);
            itoa(nArg, &pcBuffer, 16);
            cIndex = *(++pcIndex);
            break;
        default:
            break;
        }
    }
    *pcBuffer = '\000';
    return pcBuffer - lpszBuffer;
}

INT RtlSPrintf(LPSTR lpszBuffer, LPCSTR lpszFormat, ...) {
    va_list lpcList;
    REG INT nRetval;
    va_start(lpcList, lpszFormat);
    nRetval = RtlVSPrintf(lpszBuffer, lpszFormat, lpcList);
    va_end(lpcList);
    return nRetval;
}

INT RtlVSScanf(LPSTR lpszSrc, LPCSTR lpszFormat, va_list lpcList) {
    LPSTR pcBuffer = lpszSrc;
    LPCSTR pcIndex = lpszFormat;
    CHAR cIndex = *pcIndex;
    LPSTR pcArg;
    while (cIndex) {
        if (cIndex != '%') {
            cIndex = *(++pcIndex);
            continue;
        }
        cIndex = *(++pcIndex);
        switch (cIndex) {
        case 's':
            pcArg = va_arg(lpcList, LPSTR);
            while (*pcBuffer == ' ')
                pcBuffer++;
            while (*pcBuffer != ' ' && *pcBuffer)
                *(pcArg++) = *(pcBuffer++);
            *pcArg = '\000';
            break;
        case 'c':
            *va_arg(lpcList, LPSTR ) = *(pcBuffer++);
            break;
        case 'd':
            *va_arg(lpcList, INT *) = atoi(&pcBuffer);
            break;
        default:
            break;
        }
    }
    return pcBuffer - lpszSrc;
}

INT RtlSScanf(LPSTR lpszSrc, LPCSTR lpszFormat, ...) {
    va_list lpcList;
    REG INT nRetval;
    va_start(lpcList, lpszFormat);
    nRetval = RtlVSScanf(lpszSrc, lpszFormat, lpcList);
    va_end(lpcList);
    return nRetval;
}

INT RtlStringCompare(LPCSTR lpszStr1, LPCSTR lpszStr2) {
    REG DWORD dwIndex;
    for (dwIndex = 0; lpszStr1[dwIndex]; dwIndex++)
        if (lpszStr1[dwIndex] == lpszStr2[dwIndex])
            continue;
        else
            return lpszStr1[dwIndex] > lpszStr2[dwIndex] ? 1 : -1;
    return lpszStr2[dwIndex] == 0
                ? 0
                : (lpszStr1[dwIndex] > lpszStr2[dwIndex] ? 1 : -1);
}

INT RtlStringNCompare(LPCSTR lpszStr1, LPCSTR lpszStr2,
                      DWORD dwLength) {
    REG DWORD dwIndex;
    for (dwIndex = 0; lpszStr1[dwIndex] && dwIndex < dwLength; dwIndex++)
        if (lpszStr1[dwIndex] == lpszStr2[dwIndex])
            continue;
        else
            return lpszStr1[dwIndex] > lpszStr2[dwIndex] ? 1 : -1;
    if (dwIndex >= dwLength)
        return 0;
    return lpszStr2[dwIndex] == 0
                ? 0
                : (lpszStr1[dwIndex] > lpszStr2[dwIndex] ? 1 : -1);
}

VOID RtlStringCopy(LPSTR lpvDest, LPCSTR src) {
    REG DWORD dwIndex;
    for (dwIndex = 0; src[dwIndex]; dwIndex++)
        lpvDest[dwIndex] = src[dwIndex];
    lpvDest[dwIndex] = '\000';
}

VOID RtlStringNCopy(LPSTR lpvDest, LPCSTR src, DWORD dwLength) {
    REG DWORD dwIndex;
    for (dwIndex = 0; src[dwIndex] && dwIndex < dwLength; dwIndex++)
        lpvDest[dwIndex] = src[dwIndex];
    if (dwIndex < dwLength)
        lpvDest[dwIndex] = '\000';
}

VOID RtlReplaceChar(LPSTR lpszStr, CHAR cSrc, CHAR cDest) {
    REG DWORD dwIndex;
    for (dwIndex = 0; lpszStr[dwIndex]; dwIndex++)
        if (lpszStr[dwIndex] == cSrc)
        lpszStr[dwIndex] = cDest;
}

DWORD RtlStringLength(LPCSTR lpszStr) {
    REG DWORD dwIndex;
    for (dwIndex = 0; lpszStr[dwIndex]; dwIndex++);
    return dwIndex;
}

VOID RtlSplitString(LPSTR *ppszList, LPSTR lpszStr, CHAR cSplit) {
    REG DWORD dwIndex;
    REG DWORD dwLength;
    REG DWORD dwNum = 0;
    dwLength = RtlStringLength(lpszStr);
    RtlReplaceChar(lpszStr, cSplit, '\000');
    if (*lpszStr) {
        ppszList[dwNum++] = lpszStr;
    }
    for (dwIndex = 1; dwIndex < dwLength; dwIndex++) {
        if ((!lpszStr[dwIndex - 1]) && lpszStr[dwIndex]) {
            ppszList[dwNum++] = lpszStr + dwIndex;
        }
    }
    ppszList[dwNum] = NULL;
}
